package com.hk.webapp.utils.encryptions;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
import java.security.spec.KeySpec;
import java.util.Arrays;


@Slf4j
public class AESUtils {

    private static final String KEY_NAME = "AES";
    /**AES默认的加密算法*/
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    // 加解密算法/模式/填充方式 用于微信小程序
    // ECB模式只用密钥即可对数据进行加密解密，CBC模式需要添加一个iv
    public static final String XCX_CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
    private static final String DEFAULT_SECRET_KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
    private static final String SECRET_KEY = "WEFRD3.445DF4a65dfg12";
    /**偏移量,可自行修改*/
    private static String IV_PARAMETER = "0392039203920300";
    private static final int ITERATION_COUNT = 65536;
    private static final int KEY_LENGTH = 256;

    public static String encrypt(String content, String salt) throws Exception {
        try{
            Cipher ecipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
            ecipher.init(Cipher.ENCRYPT_MODE, getSecretKey(salt.getBytes()),iv);
            byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
            byte[] encrypted = ecipher.doFinal(bytes);
            return new BASE64Encoder().encode(encrypted);
        }catch (Exception e){
            return StringUtils.EMPTY;
        }
    }

    public static String decrypt(String content, String salt){
        try{
            Cipher dcipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            IvParameterSpec iv = new IvParameterSpec(IV_PARAMETER.getBytes());
            dcipher.init(Cipher.DECRYPT_MODE, getSecretKey(salt.getBytes()),iv);
            byte[] bytes = new BASE64Decoder().decodeBuffer(content);
            byte[] decrypted = dcipher.doFinal(bytes);
            return new String(decrypted, StandardCharsets.UTF_8);
        }catch (Exception e){
            return StringUtils.EMPTY;
        }
    }

    /**
     * 生成加密秘钥
     *
     * @return
     */
    private static SecretKeySpec getSecretKey(final byte[] salts) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance(DEFAULT_SECRET_KEY_FACTORY_ALGORITHM);
            KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), salts, ITERATION_COUNT, KEY_LENGTH);
            SecretKey tmp = factory.generateSecret(spec);
            return new SecretKeySpec(tmp.getEncoded(), KEY_NAME);// 转换为AES专用密钥
        } catch (Exception ex) {
            log.info("AES ERROR,MSG:{}", ex.getMessage());
        }

        return null;
    }


    /**
     * 微信 数据解密<br/>
     * 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充<br/>
     * 对称解密的目标密文:encrypted=Base64_Decode(encryptData)<br/>
     * 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节<br/>
     * 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节<br/>
     *
     * @param encrypted 目标密文
     * @param session_key 会话ID
     * @param iv 加密算法的初始向量
     */
    public static String wxDecrypt(String encrypted, String session_key, String iv) {
        return wxDecrypt(encrypted, session_key, iv, StandardCharsets.UTF_8.toString());
    }
    private static String wxDecrypt(String encrypted, String session_key, String iv, String encoding) {
        byte[] encrypted64 = Base64.decodeBase64(encrypted.getBytes());
        byte[] key64 = Base64.decodeBase64(session_key.getBytes());
        byte[] iv64 = Base64.decodeBase64(iv.getBytes());
        try {
            key64 = fillByteArrayTo16(key64);
            iv64 = fillByteArrayTo16(iv64);
            init();
            byte[] bytes = decrypt(encrypted64, key64, generateIV(iv64));
            return new String(bytes, encoding);
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }
    }

    private static byte[] fillByteArrayTo16(byte[] originData){
        // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
        int base = 16;
        if (originData.length % base != 0) {
            int groups = originData.length / base + (originData.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(originData, 0, temp, 0, originData.length);
            originData = temp;
        }
        return originData;
    }

    /**
     * 初始化密钥
     */
    private static void init() throws Exception {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null){
            System.out.println("security provider BC not found");
            Security.addProvider(new BouncyCastleProvider());
        }
        KeyGenerator.getInstance(KEY_NAME).init(128);
    }

    /**
     * 生成iv
     */
    private static AlgorithmParameters generateIV(byte[] iv) throws Exception {
        // iv 为一个 16 字节的数组，这里采用和 iOS 端一样的构造方法，数据全为0
        // Arrays.fill(iv, (byte) 0x00);
        AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_NAME);
        params.init(new IvParameterSpec(iv));
        return params;
    }

    /**
     * 生成解密
     */
    private static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv)
            throws Exception {
        Key key = new SecretKeySpec(keyBytes, KEY_NAME);
        Cipher cipher = Cipher.getInstance(XCX_CIPHER_ALGORITHM);
        // 设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(encryptedData);
    }

    public static void main(String[] args) throws Exception {

        String message = "123456";
        String salt = "c3551eb342ee4dab8abd5e81802a2b62";

        String encrypted = AESUtils.encrypt(message,salt);
        System.out.println("Encrypt(\"" + message + "\", \"" + salt + "\") = \"" + encrypted + "\"");
        String decrypted = AESUtils.decrypt(encrypted,salt);

        System.out.println("Decrypt(\"" + encrypted + "\", \"" + salt + "\") = \"" + decrypted + "\"");

        String appId= "wx94f481d040b8d37d";
        String Secret = "ab0b79d3ae6f033f0720c00d69d3353a";
        String encryptedDataStr = "/OtJGly8CzEtRpky2a31GKMyFKnhjqbcp4qM8CxqqFVSgqVW+YmClid0lNS8Vc8aZaObMhVFhc6vApnue/EGS0ANO24Gc++zvFQR6/C7zDejdd1DjCMl4QKXQT9AeIZFhOIbhGHMxwJTNpQLEipOZTg9ymn5EbADTFvVIOBGgwzG9bH1/E5VGKftCACWIyyZL2YsDSQM7dgH1wXB12559M2fyGVRKYqXmE8gaco3ak+xRkUqbziC0sKVMs5j8RT0YnFDxNkfe7pHARH3lA6c1wbpS81BoFzyyJoglsx0MPXzSTbae5CDpziUtU8iocNjWyRfBoXeuCtNxlybmCqU6p4V51VbQ0PV/P5GOgIIStjW2F6Rs2AZ/AYNSYGVi1/vw4MXnhAO5oY1tMMirHAHDv2zbO8GW7ysv4Xg8kHkHJxmIxzZlJQfO5ffE0/j/0udPTkpJgHoU4mqwC6RSKIh8Q==";
        String key = "c8f8YjaPsYzBfyg/UxiLSg==";
        String iv = "yxd3Fxj6MbTjj+k55qW7Yw==";
        String s = wxDecrypt(encryptedDataStr, key, iv);
        System.out.println(s);
    }

}
